import time
import abc

import absl
from absl import flags
import memcache

from coin.base.hostname import get_hostname
import coin.exchange.base.kr_rest.product as base_product
import coin.exchange.base.kr_rest.futures_product as base_futures_product
from coin.exchange.base.kr_rest.value_cacher import ValueCacher
from coin.exchange.base.kr_rest.value_cacher import ValueMemCacher

flags.DEFINE_string('order_memcached_serverspec', None, 'memcached ServerSpec')


def _gen_memcached_client():
  serverspec = None
  try:
    serverspec = flags.FLAGS.order_memcached_serverspec
  except absl.flags._exceptions.UnparsedFlagAccessError:
    pass
  if serverspec is not None:
    mc_spec = serverspec.split(',')
    mc = memcache.Client(mc_spec)
    assert mc is not None, mc
    return mc
  else:
    return None


_product_class = (base_product.NativeProduct, base_futures_product.NativeFuturesProduct)


class OrderBase(metaclass=abc.ABCMeta):
  ProductType = None

  def __init__(self, products, key_file):
    assert issubclass(self.ProductType, _product_class)

    if not isinstance(products, list):
      products = [products]
    self.products_request = products
    self.products = [self.ProductType.FromAny(product) for product in products]
    assert all([isinstance(product, _product_class) for product in self.products])

    self.balance_cacher = ValueCacher(2.0)
    self.balance_memcacher = None
    mc = _gen_memcached_client()
    if mc is not None:
      key = '_'.join([get_hostname(), self.ProductType._exchange, key_file])
      self.balance_memcacher = ValueMemCacher(2.0, key, mc)

  def get_products(self):
    return self.products

  def get_balance_cached(self):
    now = time.time()
    value = None
    if value is None and self.balance_cacher is not None:
      value = self.balance_cacher.get(now)
    if value is None and self.balance_memcacher is not None:
      value = self.balance_memcacher.get(now)
    return value

  def get_product(self, symbol):
    return self.ProductType.FromStr(symbol)

  def query_balance(self):
    now = time.time()
    value = self.query_balance_impl()
    if value is not None and self.balance_cacher is not None:
      self.balance_cacher.update(value, now)
    if value is not None and self.balance_memcacher is not None:
      self.balance_memcacher.update(value, now)
    return value

  @abc.abstractmethod
  def query_balance_impl(self):
    raise NotImplementedError()

  @abc.abstractmethod
  def query_submit(self, product, side, price, qty):
    raise NotImplementedError()

  @abc.abstractmethod
  def query_cancel(self, product, order_id):
    raise NotImplementedError()

  @abc.abstractmethod
  def query_cancel_product(self, product):
    raise NotImplementedError()


class TypedOrderBase(OrderBase):
  ProductType = None
  PrivateClientType = None

  def __init__(self, products, key_file):
    OrderBase.__init__(self, products, key_file)
    self.client = self.PrivateClientType(key_file)
    self.last_order = None

  def query_balance_impl(self):
    return self.client.query_account_balance()

  def query_submit(self, product, side, price, qty):
    self.last_order = self.client.query_submit(product, side, price, qty)
    return self.last_order

  def query_submit_ioc(self, product, side, price, qty):
    self.last_order = self.client.query_submit_ioc(product, side, price, qty)
    return self.last_order

  def query_cancel(self, product, order_id):
    return self.client.query_cancel(product, order_id)

  def query_cancel_product(self, product):
    return self.client.query_cancel_product(product)

  def query_list_orders(self, product):
    return self.client.query_list_orders(product)
